/**
 * @file        rl.cpp
 * @brief       Console reader.
 *
 * @author      Emre Turkay <emreturkay at gmail>
 * @date        Nov.14.2008
 *
 * Copyright Emre Turkay 2009
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file COPYRIGHT or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#include "rl.hpp"

#include <readline/history.h>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string/trim.hpp>

namespace rl
{

reader& reader::instance()
{
    if (instance_)
        return *instance_;
    else
        return *(instance_ = new reader());
}

std::string reader::read(bool& eof, const char* prompt)
{
    std::string result;

    char* line;
    line = readline(prompt);
    eof = !line;
    if (line)
    {
        result = line;
        free(line);
    }
    boost::trim(result);

    if (result.size())
        add_history(result.c_str());

    return result;
}

reader::reader()
{
    rl_attempted_completion_function = &reader::completion;
    rl_bind_key('\t', rl_complete);
    rl_completion_entry_function = &reader::generator;
    rl_basic_word_break_characters = " \t\n\"'";
}

char** reader::completion(const char* text, int start, int end)
{
    if (!instance_->completer_.empty())
    {
        char *tmp = rl_copy_text(0, end);
        std::string s(tmp);

        std::vector<std::string> args;
        typedef boost::escaped_list_separator<char> tokenizer_func;
        boost::tokenizer<tokenizer_func>
            tok(s, tokenizer_func("\\", " \t", "\""));
        std::copy(tok.begin(), tok.end(), std::back_inserter(args));

        instance_->matches_ = instance_->completer_(args, start == end);
        free(tmp);

        return rl_completion_matches(text, &reader::generator);
    }

    return rl_completion_matches(text, &reader::null_generator);
}

char* reader::generator(const char*, int state)
{
    if (!state)
        instance_->generator_index_ = 0;
    else
        ++instance_->generator_index_;

    if (instance_->generator_index_ >= int(instance_->matches_.size()))
        return 0;
    else
        return strdup(instance_->matches_[instance_->generator_index_].c_str());
}

char* reader::null_generator(const char*, int)
{
    return 0;
}

reader* reader::instance_ = 0;

} // namespace rl

